import { Mut, Computed, VueService } from 'vue3-oop';
import { Graph,Cell,Shape,Node, Edge } from "@antv/x6";
import { Snapline } from "@antv/x6-plugin-snapline";
import { Dnd } from "@antv/x6-plugin-dnd";
import { History } from "@antv/x6-plugin-history";
import { Scroller } from "@antv/x6-plugin-scroller";
import { Selection } from "@antv/x6-plugin-selection";
import { Export } from "@antv/x6-plugin-export";

import { registerElements,BpmnShapeNames,BpmnElements } from './elements';
import { convertXml, convertGraph } from './utils';
import type { ProcessProps } from './utils';

registerElements();

export default class BpmnModeler extends VueService{
  private graph:Graph;
  private dnd:Dnd;
  constructor(container:HTMLElement){
    super();
    this.graph = this.initGraph(container);
    this.dnd = this.initDnd();
  }
  private initGraph = (container:HTMLElement):Graph=>{
    const graph = new Graph({
      container: container,
      background: {
        color: "whitesmoke",
      },
      connecting: {
        router: 'manhattan',
        connector: 'rounded',
        allowMulti: false,
        allowBlank: false,
        allowLoop: false,
        allowEdge: false
      },
      autoResize: true,
      mousewheel: {// 鼠标滚轮缩放
        enabled: true,
        factor: 1.05
      },
      grid: {
        visible: true,
        type: "doubleMesh",
        args: [
          {
            color: "#eee", // 主网格线颜色
            thickness: 1, // 主网格线宽度
          },
          {
            color: "#ddd", // 次网格线颜色
            thickness: 1, // 次网格线宽度
            factor: 4, // 主次网格线间隔
          },
        ],
      },
    });
    
    graph.options.connecting.createEdge = ()=>graph.createEdge({shape:BpmnElements.SequenceFlow})
    graph.use(new Snapline({enabled: true}));
    graph.use(new History({enabled: true}));
    graph.use(new Scroller({enabled: true ,pannable:true}));
    graph.use(new Selection({enabled: true,showNodeSelectionBox:true,showEdgeSelectionBox:true}));
    graph.use(new Export());

    graph.on('blank:click',({ e, x, y })=>{this.selectedId = this.processProps.id;this.selectedCell = {} as Cell;});
    graph.on('cell:click',({ e, x, y, cell, view })=>{this.selectedId = cell.id;this.selectedCell = cell;});
    graph.on("edge:connected", ({ edge, isNew }) => {
      if(edge.getTargetNode()?.shape===BpmnElements.CommitNode && edge.getTargetNode() && edge.getSourceNode()){
        const sourceNode = edge.getTargetNode() as Node, targetNode = edge.getSourceNode() as Node;
        if(graph.getEdges().find(edge=>edge.getSourceCellId()===sourceNode.id&&edge.getTargetCellId()===targetNode.id)){
          graph.removeEdge(edge.id);
        }else{
          edge.setSource(sourceNode as any);
          edge.setTarget(targetNode as any);
        }
      }
      if(edge.getSourceNode()){
        const sourceNode = edge.getSourceNode() as Node;
        sourceNode.data = sourceNode.data || {};
        const defaultEdges = graph.getOutgoingEdges(sourceNode.id)?.filter((outEdge)=>outEdge.id===sourceNode.data.default);
        if(!sourceNode.data.default || !defaultEdges || defaultEdges.length==0){
          sourceNode.data.default = edge.id;
        }
      }
    });
    
    return graph;
  }
  private initDnd = ():Dnd=>{
    return new Dnd({
      target: this.graph,
      validateNode: (droppingNode,options)=>{
        if(droppingNode.shape===BpmnElements.CommitNode){
          if(this.graph.getNodes().filter(node=>node.shape===BpmnElements.CommitNode).length>0){
            window.$message.warning("只能添加一个流程开始节点！");
            return false;
          }
        }else if(droppingNode.shape===BpmnElements.FinishNode){
          if(this.graph.getNodes().filter(node=>node.shape===BpmnElements.FinishNode).length>0){
            window.$message.warning("只能添加一个流程结束节点！");
            return false;
          }
        }
        return true;
      }
    });
  }

  public getGraph=():Graph=>this.graph;
  public getDnd=():Dnd=>this.dnd;
  public getBpmnShapeName=(type:string):string=>BpmnShapeNames[type] || type;

  @Mut() processProps:ProcessProps = {
    id: 'NEW_PROCESS',
    name: '新建流程',
    isExecutable: true,
    candidateStarterUsers: '',
    candidateStarterGroups: '',
    documentation: '',
    isEagerExecutionFetching: 'false',
    formProperty: []
  }

  public getGraphXml=()=>{
    return convertXml(this.processProps,this.graph.getNodes(),this.graph.getEdges());
  }

  public setGraphXml=(xml:string)=>{
    const { processProps,nodes,edges } = convertGraph(xml);
    this.processProps = processProps;
    this.selectedId = this.processProps.id;
    this.graph.clearCells();
    const cells: Cell[] = [];
    nodes.forEach((node)=>{
      cells.push(this.graph.createNode(node));
    })
    edges.forEach((edge)=>{
      cells.push(this.graph.createEdge(edge));
    })
    setTimeout(()=>{this.graph.resetCells(cells);},500)
  }

  @Mut() selectedId = this.processProps.id;
  @Mut() selectedCell:Cell = {} as Cell;

  @Computed() get selectedName(){
    if(this.selectedId===this.processProps.id){
      return this.processProps.name;
    }else if(this.graph.getCellById(this.selectedId).isEdge()){
      return (this.graph.getCellById(this.selectedId) as any)?.labels?.[0]?.attrs.label.text || '';
    }else{
      return (this.graph.getCellById(this.selectedId) as any)?.label || '';
    }
  }
  @Computed() get selectedShape(){
    return this.selectedId===this.processProps.id?"Process":this.graph.getCellById(this.selectedId).shape;
  }

  public updateCellId=(cell:Cell,newId:string)=>{
    if(this.graph.getCellById(newId)){
      window.$message.warning(`ID（${newId}）已经被占用，忽略更新！`)
      return;
    }
    const oldId = cell.id;
    if(cell.isEdge()){
      const sourceNode = (cell as Edge).getSourceNode();
      if(sourceNode?.data?.default===oldId){
        sourceNode.data.default = newId;
      }
    }
    this.graph.updateCellId(cell,newId);
    if(this.selectedId = oldId){this.selectedId = newId;}
  }

  public clearCells=()=>{
    this.graph.clearCells();
    this.selectedId = this.processProps.id;
  }
}